using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using UnityEngine;

namespace CGF
{
    public class CGFramework : MonoBehaviour
    {
        private static CGFramework _instance;

        public static CGFramework Instance
        {
            get
            {
                if (!_instance)
                {
                    _instance = FindObjectOfType<CGFramework>();
                    if (!_instance)
                    {
                        GameObject go = new GameObject(nameof(CGFramework));
                        _instance = go.AddComponent<CGFramework>();
                    }
                }

                return _instance;
            }
        }

        public event Action updateAction;
        public Assembly assembly { get; private set; }

        void Awake()
        {
            if (Application.isPlaying)
            {
                DontDestroyOnLoad(gameObject);
            }

            //预置打包类型，避免后期热更时无法使用
            ReferentClasses.Init(this);
        }

        /// <summary>
        /// 开始运行框架，初始化完毕后将首先触发热更工程的HotfixMain.Run()方法
        /// </summary>
        public async void Run()
        {
            //补充元数据
            if (!Application.isEditor && CGFConfig.Instance.ResStrategy != EResStrategy.Resources)
            {
                byte[] metadataABBytes = null;
                if (Define.IsWebGL && !Application.isEditor)
                {
                    CTask<byte[]> task = CTask<byte[]>.Create();
                    IndexedDBHelper.GetValueByKey(Define.MetadataAbName + Define.AbExtName, bytes =>
                    {
                        task.SetResult(bytes);
                    });
                    metadataABBytes = await task;
                }
                else
                {
                    string metaDataPath = Define.AbCachePath + "/" + Define.MetadataAbName + Define.AbExtName;
                    if (File.Exists(metaDataPath))
                    {
                        metadataABBytes = File.ReadAllBytes(metaDataPath);
                    }
                }
                
                if(metadataABBytes == null)
                {
                    metadataABBytes = await StreamingAssetsHelper.GetBytes($"{Define.MetadataAbName}{Define.AbExtName}");
                    if (metadataABBytes == null)
                    {
                        Debug.LogError($"StreamingPath里不存在{Define.MetadataAbName}{Define.AbExtName}");
                        return;
                    }
                }

                LoadMetadataForAOTAssemblies(metadataABBytes);
            }

            //加载热更
            if (CGFConfig.Instance.UseAB && CGFConfig.Instance.ResStrategy != EResStrategy.Resources)
            {
                TextAsset dllAsset = await ResManager.Instance.LoadAsset<TextAsset>($"Code/{Define.DllAssetName}.dll.bytes");
                assembly = Assembly.Load(dllAsset.bytes);
            }
            else
            {
                assembly = AppDomain.CurrentDomain.GetAssemblies().First(assembly => assembly.GetName().Name == Define.DllAssetName);
                if (assembly == null)
                {
                    Debug.LogError($"离线模式加载{Define.DllAssetName}失败");
                }
            }

            RunHotfixCode();
        }

        private void RunHotfixCode()
        {
            Type HotfixLaunch = assembly.GetType("CGF.HotfixLaunch");
            MethodInfo method = HotfixLaunch.GetMethod("Run");
            method.Invoke(null, null);
        }

        private static void LoadMetadataForAOTAssemblies(byte[] metadataBytes)
        {
            AssetBundle metadataAB = AssetBundle.LoadFromMemory(metadataBytes);
            Debug.Log($"补充元数据dll数量: {metadataAB.GetAllAssetNames().Length}");
            foreach (var dllName in metadataAB.GetAllAssetNames())
            {
                byte[] dllBytes = metadataAB.LoadAsset<TextAsset>(dllName).bytes;
                var result = HybridCLR.RuntimeApi.LoadMetadataForAOTAssembly(dllBytes, HybridCLR.HomologousImageMode.SuperSet);
                //Debug.Log($"补充元数据: {dllName} -> {result}");
            }
        }

        public void Update()
        {
            updateAction?.Invoke();
        }
    }
}
